package com.taldh.algorithm.mutli.thread;

import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

class FooBar {
    /**
     * 我们提供一个类：
     *
     * class FooBar {
     *   public void foo() {
     *     for (int i = 0; i < n; i++) {
     *       print("foo");
     *     }
     *   }
     *
     *   public void bar() {
     *     for (int i = 0; i < n; i++) {
     *       print("bar");
     *     }
     *   }
     * }
     * 两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法，另一个线程将会调用 bar() 方法。
     *
     * 请设计修改程序，以确保 "foobar" 被输出 n 次。
     *
     *  
     *
     * 示例 1:
     *
     * 输入: n = 1
     * 输出: "foobar"
     * 解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法，"foobar" 将被输出一次。
     * 示例 2:
     *
     * 输入: n = 2
     * 输出: "foobarfoobar"
     * 解释: "foobar" 将被输出两次。
     *
     * 来源：力扣（LeetCode）
     * 链接：https://leetcode-cn.com/problems/print-foobar-alternately
     * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
     */


//    private int n;
//
//
//    public FooBar(int n) {
//        this.n = n;
//    }
//
//    private volatile boolean mod = true;
//
//    public void foo(Runnable printFoo) throws InterruptedException {
//
//        for (int i = 0; i < n; i++) {
//            // printFoo.run() outputs "foo". Do not change or remove this line.
//            while (!mod) {
//                //Thread.sleep(10);
//            }
//            printFoo.run();
//            mod = false;
//        }
//    }
//
//    public void bar(Runnable printBar) throws InterruptedException {
//
//        for (int i = 0; i < n; i++) {
//            // printBar.run() outputs "bar". Do not change or remove this line.
//            while (mod) {
//                //Thread.sleep(10);
//            }
//            printBar.run();
//            mod = true;
//        }
//    }

    private int n;

    public FooBar(int n) {
        this.n = n;
    }

    private Semaphore fooSemaphore = new Semaphore(1);
    private Semaphore barSemaphore = new Semaphore(0);

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            // printFoo.run() outputs "foo". Do not change or remove this line.
            fooSemaphore.acquire();
            printFoo.run();
            barSemaphore.release();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            // printBar.run() outputs "bar". Do not change or remove this line.
            barSemaphore.acquire();
            printBar.run();
            fooSemaphore.release();
        }
    }

    public static void main(String[] args) {
        FooBar fooBar = new FooBar(2);
        Runnable runnableFoo = () -> {
            try {
                fooBar.foo(new PrintFoo());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Runnable runnableBar = () -> {
            try {
                fooBar.bar(new PrintBar());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        new Thread(runnableFoo).start();
        new Thread(runnableBar).start();
    }

}